<?php
/**
 * Memcached 行数据缓存
 * 
 * 限制条件：
 * 1、每张表必须有主键。
 * 2、依赖主键查询
 * 
 * 缓存以用户为中心, 分模块存储。
 * 
 * 需要缓存的数据主要分两部分:
 * 第一部分是数据缓存A, 在A中KEY是数据库具体模块行数据的ID，Value是模块数据库ID对应的行数据。
 * 第二部分是以用户为单位的模块数据ID列表缓存B, 在B中KEY是各模块对应下的Uid, Value是该Uid对应的该模块下所有数据ID组成的列表。
 * 
 * @example： 
 * 获取Uid为26的用户的所有好友信息：
 * 1): $ids = CacheRow::getIds('friends', 26); 
 * 2): $data = CacheRow::getRow('friends', $ids);
 * 
 * 获取Uid为26的用户的最新的五条日志信息：
 * 1): $ids = CacheRow::getIds('blog', 26);
 * 	   $ids = array_slice($ids, 0, 5); 
 * 2): $data = CacheRow::getRow('blog', $ids);
 * 
 * @author jiaowei <jiaowei@178.com>
 * @version $Id: CacheRow.php 3721 2010-02-25 07:42:56Z jiaowei $
 */

class CacheRow
{
	/**
	 * 缓存前缀
	 *
	 */
    const CACHE_PREFIX 	 = 'nga';
    
    /**
     * 缓存开关 true:关闭缓存功能
     *
     */
    const CACHE_CLOSE	 = false;
    
    /**
     * 缓存生存期
     *
     */
    const CACHE_LEFTTIME = 0;
   
   /**
     * 获取详细信息
     *
     * @param str $module	模块ID
     * @param int $mid		模块内每一条信息ID 可以是单个ID也可以是一个ID列表
     * @param int $leftTime 获取不到cache时设置cache有效时间
     * @return array
     */
    public static function getRow($module, $mid, $leftTime = self::CACHE_LEFTTIME)
    {	
    	if (empty($mid)) {
    		return false;
    	}
    	$mid = (array) $mid;
     
    	$cache = array();
        $key   = array();
        foreach ($mid AS $id) {
        	$cacheId = self::_makeRowKey($module, $id);
        	$key[] = $cacheId;
        	$cache[$cacheId] = $id;
        }
        
        //获取列表
        if ( self::CACHE_CLOSE || !($result = Mem::getInstance()->get($key)) ) {
//        	echo 'no cache';
        	
            $mid = implode(',', $mid);
            if (!$mid) {
            	return false;
            }
            $model  = self::_getModelName($module);
        	$result = Model::factory($model)->getRow($mid);
            if ($result) {
                foreach ((array) $result AS $k => $v) {
                    self::setRow($module, $v['id'], $v, $leftTime);
                }
            }
        }

        //获取的键值对缺少
        if ((count($result) != count($key))) {
//        	echo 'not all cache';
            $cachedKey  = array_keys($result);
            $noCacheKey = array_diff($key, $cachedKey);
            
            $ids = array();
            if ($noCacheKey) {
                foreach ((array) $noCacheKey AS $k => $v) {
                    $ids[] = $cache[$v];
                }
                $ids = implode(',', $ids);
            }
            
            if (!$ids) {
            	return false;
            }
            
            $model  = self::_getModelName($module);
        	$tem = Model::factory($model)->getRow($ids);   
            if ($tem) {
            	$missResult = array();
                foreach ((array) $tem AS $k => $v) {
                    self::setRow($module, $v['id'], $v, $leftTime);
                    $cacheId = self::_makeRowKey($module, $v['id']);
                    $missResult[$cacheId] = $v;
                }
                $result = $result + $missResult;
            } 
        }
        
        return $result;
    }
    
    /**
     * 获取分页数据
     *
     * @param str $module
     * @param array $ids
     * @param int $pageSize
     * @return array
     */
    public function getPageRow($module, $uid, $pageSize, $leftTime = self::CACHE_LEFTTIME)
    {
    	$ids = self::getIds($module, $uid);
    	
    	//当前页
    	$currPage = _('page', 1);
    	
    	//总页数
		$totalPage = ceil(count($ids) / $pageSize);
		
		//array 偏移量		
		$offset = ($currPage - 1) * $pageSize;
		
		$result = array();
		if ($ids) {
			//获取当前页显示的Id
			$pageIds = array_slice((array)$ids, $offset, $pageSize);
			$result = self::getRow($module, $pageIds, $leftTime);
		}
		
		return array(
			'data' => $result, 
			'pageinfo' => array('PAGE' => $currPage, 'PAGECOUNT' => $totalPage)
		);
    }
    
    /**
     * 设置缓存
     *
     * @param str $module
     * @param int $id
     * @param str|array $data
     * @param int $leftTime
     */
    public static function setRow($module, $id, $data, $leftTime = self::CACHE_LEFTTIME)
    {
    	if (self::CACHE_CLOSE) {
    		return false;
    	}
    	$key = self::_makeRowKey($module, $id);
        return Mem::getInstance()->set($key, $data, $leftTime);
    }
    
    /**
     * 删除缓存
     *
     * @param str $module
     * @param int $bid
     */
    public static function removeRow($module, $id)
    {
    	if (self::CACHE_CLOSE) {
    		return false;
    	}
        $key = self::_makeRowKey($module, $id);
        return Mem::getInstance()->remove($key);
    }
    
    

    
    /**
     * 获取列表
     *
     * @param str $module
     * @param int $uid
     * @return array
     */
    public static function getIds($module, $uid, $leftTime = self::CACHE_LEFTTIME)
    {
        $key = self::_makeIdsKey($module, $uid);
        
        if ( self::CACHE_CLOSE || !($result = Mem::getInstance()->get($key)) ) {
        	//echo 'no cache list';
        	$model  = self::_getModelName($module);
        	
        	$result = Model::factory($model)->getIds($uid);
            if ($result) {
            	self::setIds($module, $uid, $result, $leftTime);
            }
        }
        	
        return $result;
	}
    
    /**
     * 设置列表
     *
     * @param str $module
     * @param int $uid
     * @param array $data
     * @param int $leftTime
     */
    public static function setIds($module, $uid, $data, $leftTime = self::CACHE_LEFTTIME)
    {
    	if (self::CACHE_CLOSE) {
    		return false;	
    	}
    
    	$key  = self::_makeIdsKey($module, $uid);
        return Mem::getInstance()->set($key, $data, $leftTime);
	}
	
	/**
	 * 添加id到ids列表
	 *
	 * @param str $module
	 * @param int $uid
	 * @param int $id
	 * @return bool
	 */
	public static function incrIds($module, $uid, $id, $leftTime = self::CACHE_LEFTTIME)
	{
		//模块用户Ids列表
		$ids = self::getIds($module, $uid, $leftTime);

		//翻转ids
		$tem = array_flip($ids);
		
		if (!array_key_exists($id, $tem)) {
			//添加id到ids的最前面
			array_unshift($ids, $id);
			
			return self::setIds($module, $uid, $ids, $leftTime);
		}

		return true;
	}
	
	/**
	 * 减少id从ids
	 *
	 * @param str $module
	 * @param int $uid
	 * @param int $id
	 * @return bool
	 */
	public static function decrIds($module, $uid, $id, $leftTime = self::CACHE_LEFTTIME)
	{
		$ids = self::getIds($module, $uid, $leftTime);

		//翻转ids
		$tem = array_flip($ids);

		if (array_key_exists($id, $tem)) {
			//删除被删除的id
			unset($ids[$tem[$id]]);
			
			return self::setIds($module, $uid, $ids, $leftTime);
		}

		return true;
	}
	
	
	/**
	 * 删除列表
	 *
	 * @param str $module
	 * @param int $uid
	 */
	public static function removeIds($module, $uid)
	{
		if (self::CACHE_CLOSE) {
			return false;
		}
		$key = self::_makeIdsKey($module, $uid);
        return Mem::getInstance()->remove($key);
	}
	
	
	
	/**
	 * 创建列表key
	 *
	 * @param str $module
	 * @param int $uid
	 * @return str
	 */
	private static function _makeIdsKey($module, $uid)
	{
		return self::CACHE_PREFIX . '_' . $module . '_list_' . $uid;
	}
	
	/**
     * 创建行数据cache key
     *
     * @param str $module 模块ID
     * @param int $id	  模块内唯一ID
     * @return str
     */
    private static function _makeRowKey($module, $id)
    {
    	return self::CACHE_PREFIX . '_' . $module . '_' . $id;
    }
    
    /**
     * 返回模型名称
     *
     * @param str $module
     * @return str
     */
    private static function _getModelName($module)
    {
    	//子模块
    	if (false !== strpos($module, '_')) {
    		list($mainModule, $subModule) = explode('_', $module);
    		return $mainModule . '_model_cache_' . $subModule;
    	}
		
    	return $module . '_model_cache_' . $module;
    }
}
